﻿#if !CORE
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
#endif
using System.Collections.Concurrent;
using Net.System;

namespace Net.AOI
{
    /// <summary>
    /// 格子类型 -- 支持2D和3D游戏
    /// </summary>
    public enum GridType
    {
        /// <summary>
        /// 水平方式的格子, 用于3D游戏
        /// </summary>
        Horizontal,
        /// <summary>
        /// 垂直方式的格子, 适用2D游戏
        /// </summary>
        Vertical
    }

    /// <summary>
    /// 九宫格世界
    /// </summary>
    [Serializable]
    public class GridWorld
    {
        public Grid[] Grids = new Grid[0];
        public FastListSafe<IGridObject> Objects = new();
        public Rect WorldSize;
        public GridType GridType = GridType.Horizontal;
        /// <summary>
        /// 当越界提示事件
        /// </summary>
        public Action<IGridObject> OnOverflow;

        /// <summary>
        /// 初始化九宫格
        /// </summary>
        /// <param name="xPos">x开始位置</param>
        /// <param name="zPos">z开始位置</param>
        /// <param name="xMax">x列最大值</param>
        /// <param name="zMax">z列最大值</param>
        /// <param name="width">格子长度</param>
        /// <param name="height">格子高度</param>
        public void Init(float xPos, float zPos, uint xMax, uint zMax, int width, int height)
        {
            Init(new Vector2(xPos, zPos), new Vector2(width, height), new Vector2(width * xMax, height * zMax));
        }

        /// <summary>
        /// 初始化九宫格网格
        /// </summary>
        /// <param name="offset">网格偏移</param>
        /// <param name="gridSize">格子列大小</param>
        /// <param name="worldSize">网格世界大小</param>
        public void Init(Vector2 offset, Vector2 gridSize, Vector2 worldSize)
        {
            int rowLength = (int)Math.Ceiling(worldSize.x / gridSize.x);
            int colLength = (int)Math.Ceiling(worldSize.y / gridSize.y);
            int totalCells = rowLength * colLength;
            Grids = new Grid[totalCells];
            WorldSize = new Rect(offset, worldSize);
            for (int i = 0; i < totalCells; i++)
            {
                int x = i % rowLength;  // 当前列
                int y = i / rowLength;  // 当前行
                var pos = new Vector2(offset.x + x * gridSize.x, offset.y + y * gridSize.y);
                Grids[i] = new Grid(i, new Rect(pos, gridSize));
            }
            for (int i = 0; i < Grids.Length; i++)
            {
                var grid = Grids[i];
                int centerX = i % rowLength; // 当前网格列号 
                int centerY = i / rowLength; // 当前网格行号
                for (int dy = -1; dy <= 1; dy++) // 遍历周围3x3区域（包括自身）
                {
                    for (int dx = -1; dx <= 1; dx++)
                    {
                        int nx = centerX + dx;
                        int ny = centerY + dy;
                        if (nx >= 0 && nx < rowLength && ny >= 0 && ny < colLength)
                        {
                            int neighborIndex = ny * rowLength + nx;
                            ArrayHelper.Add(ref grid.Grids, Grids[neighborIndex]);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 插入物体到九宫格感兴趣区域
        /// </summary>
        /// <param name="gridObject"></param>
        /// <returns></returns>
        public bool Insert(IGridObject gridObject)
        {
            if (gridObject.Grid != null) //防止多次插入
                return false;
            gridObject.OnInit();
            var grid = GetGridByPosition(gridObject.Position);
            if (grid == null)
                return false;
            grid.Objects.Add(gridObject);
            gridObject.Grid = grid;
            if (gridObject.Identity == 0)
                gridObject.Identity = Objects.Count;
            Objects.Add(gridObject);
            for (int j = 0; j < grid.Grids.Length; j++)
            {
                var grid1 = grid.Grids[j];
                gridObject.OnEnterGrid(grid1);
                for (int k = 0; k < grid1.Objects.Count; k++) //通知新格子, 此物体进来了
                {
                    var item2 = grid1.Objects[k];
                    EnterHandler(gridObject, item2);
                }
            }
            return true;
        }

        /// <summary>
        /// 根据位置快速查找所属Grid
        /// </summary>
        /// <param name="position">查询位置</param>
        /// <returns>所属Grid，如果越界则返回null</returns>
        public Grid GetGridByPosition(Vector3 position)
        {
            if (!(GridType == GridType.Horizontal ? WorldSize.ContainsXZ(position) : WorldSize.Contains(position)))
                return null;
            var cellWidth = Grids[0].Rect.width;
            var cellHeight = Grids[0].Rect.height;
            var row = (int)((position.x - WorldSize.x) / cellWidth);
            var col = (int)(((GridType == GridType.Horizontal ? position.z : position.y) - WorldSize.y) / cellHeight);
            var totalRows = (int)(WorldSize.width / cellWidth);
            var index = col * totalRows + row;
            return (index >= 0 && index < Grids.Length) ? Grids[index] : null;
        }

        public bool Contains(Grid grid, IGridObject gridObject)
        {
            if (GridType == GridType.Horizontal)
                return grid.Rect.ContainsXZ(gridObject.Position);
            return grid.Rect.Contains(gridObject.Position);
        }

        /// <summary>
        /// 获取物体的感兴趣九宫格区域
        /// </summary>
        /// <param name="gridObject"></param>
        /// <returns></returns>
        public Grid UpdateGrid(IGridObject gridObject)
        {
        JMP:
            var currGrid = gridObject.Grid;
            if (currGrid == null)
            {
                var grid = GetGridByPosition(gridObject.Position);
                if (grid == null)
                    goto J;
                grid.Objects.Add(gridObject);
                gridObject.Grid = grid;
                for (int j = 0; j < grid.Grids.Length; j++)
                {
                    var grid1 = grid.Grids[j];
                    gridObject.OnEnterGrid(grid1);
                    for (int k = 0; k < grid1.Objects.Count; k++) //通知新格子, 此物体进来了
                    {
                        var item2 = grid1.Objects[k];
                        EnterHandler(gridObject, item2);
                    }
                }
                return grid;
            }
            if (Contains(currGrid, gridObject))
                return currGrid;
            var oldGrids = currGrid.Grids;
            for (int i = 0; i < oldGrids.Length; i++) //遍历当前物体所在的九宫格
            {
                var grid = oldGrids[i];
                if (Contains(grid, gridObject)) //如果物体还在九宫格的其中一个格子
                {
                    var newGrids = grid.Grids;
                    for (int j = 0; j < newGrids.Length; j++) //遍历当前所在的九宫格,如果是新进来的格子, 则通知新格子此物体进来了
                    {
                        var grid1 = newGrids[j];
                        if (!oldGrids.Contains(grid1)) //如果当前为新格子, 则通知
                        {
                            gridObject.OnEnterGrid(grid1);
                            for (int k = 0; k < grid1.Objects.Count; k++) //通知新格子, 此物体进来了
                            {
                                var item2 = grid1.Objects[k];
                                EnterHandler(gridObject, item2);
                            }
                        }
                    }
                    for (int k = 0; k < oldGrids.Length; k++) //遍历当前所在的九宫格,如果已经离开的九宫格则调用离开方法
                    {
                        var grid1 = oldGrids[k];
                        if (!newGrids.Contains(grid1)) //如果已经离开了旧的格子
                        {
                            gridObject.OnLeaveGrid(grid1);
                            for (int j = 0; j < grid1.Objects.Count; j++) //通知离开的格子的所有物体, 此物体离开了
                            {
                                var item2 = grid1.Objects[j];
                                ExitHandler(gridObject, item2);
                            }
                        }
                    }
                    //更新物体所在的格子
                    currGrid.Objects.Remove(gridObject);
                    grid.Objects.Add(gridObject);
                    gridObject.Grid = grid;
                    return grid;
                }
            }
            for (int i = 0; i < oldGrids.Length; i++) //拖拽瞬移过程
            {
                var grid = oldGrids[i];
                gridObject.OnLeaveGrid(grid);
                for (int j = 0; j < grid.Objects.Count; j++) //通知离开的格子的所有物体, 此物体离开了
                {
                    var item2 = grid.Objects[j];
                    ExitHandler(gridObject, item2);
                }
            }
            currGrid.Objects.Remove(gridObject);
            gridObject.Grid = null;
            goto JMP;
        J:
            if (OnOverflow != null)
                OnOverflow(gridObject);
            else
                Event.NDebug.LogError($"{gridObject.Identity}越界了,位置:{gridObject.Position}");
            return null;
        }

        /// <summary>
        /// 移除感兴趣物体
        /// </summary>
        /// <param name="gridObject"></param>
        public void Remove(IGridObject gridObject)
        {
            var currGrid = gridObject.Grid;
            if (currGrid == null)
                return;
            var grids = currGrid.Grids;
            for (int i = 0; i < grids.Length; i++)
            {
                var grid = grids[i];
                gridObject.OnLeaveGrid(grid);
                for (int j = 0; j < grid.Objects.Count; j++) //通知离开的格子的所有物体, 此物体离开了
                {
                    var item2 = grid.Objects[j];
                    ExitHandler(gridObject, item2);
                }
            }
            currGrid.Objects.Remove(gridObject);
            Objects.Remove(gridObject);
            gridObject.Grid = null; //防止执行两次
        }

        /// <summary>
        /// 当一个物体进入另外一个物体处理
        /// </summary>
        /// <param name="gridObject"></param>
        /// <param name="other"></param>
        private static void EnterHandler(IGridObject gridObject, IGridObject other)
        {
            if (gridObject == other || other == null || gridObject.Identity == other.Identity) //当插入时会偶尔发生相同的玩家通知问题
                return;
            if (gridObject.MainRole & other.MainRole) //如果两个都是主角, 则相互通知
            {
                gridObject.OnEnter(other);
                other.OnEnter(gridObject);
            }
            else if (gridObject.MainRole) //当以自己为主角时, 这样通知
            {
                gridObject.OnEnter(other);
            }
            else other.OnEnter(gridObject);
        }

        /// <summary>
        /// 当一个物体退出另外一个物体处理
        /// </summary>
        /// <param name="gridObject"></param>
        /// <param name="other"></param>
        private static void ExitHandler(IGridObject gridObject, IGridObject other)
        {
            if (gridObject == other || other == null || gridObject.Identity == other.Identity) //当插入时会偶尔发生相同的玩家通知问题
                return;
            if (gridObject.MainRole & other.MainRole) //如果两个都是主角, 则相互通知
            {
                gridObject.OnExit(other);
                other.OnExit(gridObject);
            }
            else if (gridObject.MainRole) //当以自己为主角时, 这样通知
            {
                gridObject.OnExit(other);
            }
            else other.OnExit(gridObject);
        }

        /// <summary>
        /// 更新感兴趣的移除和添加物体
        /// </summary>
        public void Update()
        {
            for (int i = 0; i < Objects._size; i++)
            {
                var gridObject = Objects.Items[i];
                UpdateGrid(gridObject);
                if (gridObject is IGridUpdate gridUpdate)
                    gridUpdate.OnUpdate();
            }
        }

        /// <summary>
        /// 多线程更新
        /// </summary>
        public void UpdateJob()
        {
            var partitioner = Partitioner.Create(0, Objects._size);
            var result = Parallel.ForEach(partitioner, range =>
            {
                var start = range.Item1;
                var end = range.Item2;
                for (int i = start; i < end; i++)
                {
                    var gridObject = Objects.Items[i];
                    if (gridObject == null)
                        return;
                    UpdateGrid(gridObject);
                    if (gridObject is IGridUpdate gridUpdate)
                        gridUpdate.OnUpdate();
                }
            });
            while (!result.IsCompleted)
                Thread.SpinWait(1);
        }
    }
}
